﻿
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace H.Utils.DataStruct
{
    /// <summary>
    /// 表示一个非托管的内存块
    /// </summary>
    public class MemBlock : IDisposable
    {
        #region 内部变量
        /// <summary>
        /// 存储内存块实际使用的大小
        /// </summary>
        private int _Size = 0;
        #endregion


        #region 属性
        /// <summary>
        /// 读取内存块可以写入的最大容量
        /// </summary>
        public int Capacity { get; private set; }

        /// <summary>
        /// 指向非托管内存的指针
        /// </summary>
        public IntPtr Pointer { get; private set; }

        /// <summary>
        /// 内存块已经写入的数据量
        /// </summary>
        public int Size
        {
            get => _Size; set
            {
                if (value <= Capacity)
                {
                    _Size = value;
                }
                else
                {
                    throw new AccessViolationException("请先分配足够大的内存");
                }
            }
        }

        #endregion

        #region 函数
        /// <summary>
        /// 确保内存块的大小大于某个值
        /// 注意: 此函数必须在任何使用<code>Pointer</code>之前进行,调用此函数之后,原有的Pointer可能重新分配
        /// </summary>
        /// <param name="size"></param>
        /// <returns></returns>
        public IntPtr EnsureSize(int size)
        {
            if (Pointer == IntPtr.Zero)
            {
                Pointer = Marshal.AllocHGlobal(size);
                Capacity = size;
            }
            else
            {
                if (size > Capacity)
                {
                    Pointer = Marshal.ReAllocHGlobal(Pointer, (IntPtr)size);
                    Capacity = size;
                }
            }
            Size = size;

            return Pointer;
        }

        /// <summary>
        /// 将内存块转化为字节数组表示
        /// </summary>
        /// <returns></returns>
        public byte[] ToBytes()
        {
            if (Pointer == IntPtr.Zero)
            {
                throw new InvalidOperationException("必须先调用EnsureSize函数");
            }
            byte[] ret = new byte[Size];
            Marshal.Copy(Pointer, ret, 0, Size);
            return ret;
        }

        /// <summary>
        /// 将字节数组的值写入到内存块
        /// </summary>
        /// <returns></returns>
        public void SetBytes(byte[] data)
        {
            Marshal.Copy(data, 0, EnsureSize(data.Length), data.Length);
        }

        #endregion

        #region 资源释放相关
        private void Dispose(bool disposing)
        {
            if (disposing) { }
            if (Pointer != IntPtr.Zero)
            {
                Marshal.FreeHGlobal(Pointer);
                Pointer = IntPtr.Zero;
            }
        }

        public void Dispose()
        {
            Dispose(true);
        }

        ~MemBlock()
        {
            Dispose(false);
        }
        #endregion
    }
}
